#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Purpose: HydroPlatform menu classes
# Created: 04/04/2009
# $Id $
# Copyright (c) 2008,2009 by University College London
# Authors:
# Didrik Pinte --<dpinte@dipole-consulting.com>
#
# This program is free software under the GPL (>=v2)
# Read the file COPYING coming with HydroPlatform for details.
'''
HydroPlatform menu related functions and classes
'''

import wx

from Thuban import _
from Thuban.UI.command import registry, Command, ToolCommand
from Thuban.UI.mainwindow import make_check_current_tool, main_menu, \
     main_toolbar
from Thuban.UI.menu import Menu


from hydroplatform.ui.commands import identify_node_tool, add_link_tool, \
     move_node_tool, delete_node_tool, delete_network, new_network, \
     new_project, delete_project, open_network, join_network, export_network, \
     saveas_network, rename_network, connect_to_db, edit_project, \
     saveas_project, import_existing_types, edit_types, open_project, \
     add_node_with_type, label_network, is_label_activated, \
     edit_non_visible_types, network_edit
from hydroplatform.ui.renderer import getBitmapForNodeType
from hydroplatform.ui.resources import get_bitmap_resource
from hydroplatform.ui.session import has_network, has_dbsession, has_project

def move_to_thuban_menu():
    '''
    Move Thuban menu's to a Thuban entry in the menu list in order to 
    clarify the user inferface
    '''
    thuban_menu = main_menu.FindOrInsertMenu("thuban", 
                                         _("&Thuban"), after="map")
    for menu_name in ["file", "layer", "table", 
                      "experimental", "extensions"]:
        menu = main_menu.find_menu(menu_name)
        main_menu.RemoveItem(menu)
        thuban_menu.InsertItem(menu)
        

def set_hydroplatform_menu():
    '''
    Update the Thuban menus and adds HydroPlatform specific entries
    '''
    assert isinstance(main_menu, Menu)
    project_menu = main_menu.FindOrInsertMenu("project", _("&Projects"), 
                                              after="file")
    network_menu = main_menu.FindOrInsertMenu("network", _("&Networks"), 
                                              after="project")
    network_object_menu = main_menu.FindOrInsertMenu("network_object",
                                                     _("&Objects"), 
                                                     after="network")
    
    # move unneeded menu's to Thuban menu
    move_to_thuban_menu()
    
    # create new commands and register them
    
    network_menu_items = [
        ['new_network', 'New', new_network, has_project, 
         'Create a new network'],
        ['open_network', 'Open', open_network, has_project, 
         'Open an existing network'],
        ['join_network', 'Join', join_network, has_network, 
         'Join different networks'],
        ['export_network', 'Export', export_network, has_project, 
         'Export a network from project'],
        #['save_network', 'Save', save_network, has_network, 
        # 'Save network'],
        ['saveas_network', 'Save as', saveas_network, has_network, 
         'Save network as '],
        ['rename_network', 'Rename', rename_network, has_network, 
         'Rename network'],
        ['delete_network', 'Delete', delete_network, has_network, 
         'Delete network'],
        ['network_edit', 'Graph Editor', network_edit, has_network, 
         'Launch Graph Editor']
    ]
    
    project_menu_items = [
        ['connect_db', 'Connect', connect_to_db, None, 
         "Connect to database"],
        ['new_project', 'New', new_project, has_dbsession, 
         'Create a new project'],
        ['open_project', 'Open', open_project, has_dbsession, 
         'Open project'],
        ['edit_project', 'Edit', edit_project, has_project, 
         'Edit project'],
        ['saveas_project', 'Save as', saveas_project, has_project, 
         'Save project as'],
        ['delete_project', 'Delete', delete_project, has_dbsession, 
         'Delete'],
        None,
        "exit"
    ]
    
    network_object_menu_items = [
        #['new_object_type', 'New', new_type, has_project, 
        # 'New object type'],
        #['list_node_type', 'List', list_types, has_project, 
        # 'List existing types'],
        ['import_types_into_project', 
         'Import object types',
         import_existing_types, has_project, 
         'Import existing types from other projects (link or node types)'],
        #['add_existing_node', 'Add existing', add_existing_node, 
        # has_network, 'Add an existing node in the project'],
        ['type_editor', 'Object type editor', edit_types, has_project, 
         'Editing nodetypes'],
        ['non_visible_nodes', 'Non visible nodes editor', \
         edit_non_visible_types, has_project, \
         'Edit the non visible nodes']
 
    ]        
   
    menus = {project_menu : project_menu_items,
             network_menu : network_menu_items,
             network_object_menu : network_object_menu_items}
    for menu, items in menus.items():
        for item in items :
            if isinstance(item, list):
                registry.Add(Command(item[0], item[1], item[2],
                             sensitive=item[3], helptext=item[4]))
                menu.InsertItem(item[0])
            else:
                menu.InsertItem(item)
            
    identify_icon = get_bitmap_resource("identify", wx.BITMAP_TYPE_PNG)
    delete_icon   = get_bitmap_resource("remove", wx.BITMAP_TYPE_PNG)
    move_icon     = get_bitmap_resource("move", wx.BITMAP_TYPE_PNG)
    addlink_icon  = get_bitmap_resource("addlink", wx.BITMAP_TYPE_PNG)
    label_icon    = get_bitmap_resource("label_network", wx.BITMAP_TYPE_PNG)
    
    registry.Add(ToolCommand('identify_node_tool', 'Identify object', 
                 identify_node_tool,
                 helptext = "Identify node or link", 
                 icon = identify_icon,
                 sensitive = has_network,
                 checked = make_check_current_tool("IdentifyObjectTool")))
    
    registry.Add(ToolCommand('move_node_tool', 'Move node', 
                 move_node_tool,
                 helptext = "Move node", icon = move_icon,
                 sensitive = has_network,
                 checked = make_check_current_tool("MoveNodeTool")))
    
    registry.Add(ToolCommand('add_link_tool', 'Add link', 
                 add_link_tool,
                 helptext = "Add link", icon = addlink_icon,
                 sensitive = has_network,
                 checked = make_check_current_tool("AddLinkTool")))
    
    registry.Add(ToolCommand('delete_node_tool', 'Delete object', 
                 delete_node_tool,
                 helptext = "Delete node or link", icon = delete_icon,
                 sensitive = has_network,
                 checked = make_check_current_tool("DeleteObjectTool")))
    
    registry.Add(Command('label_network', 'Label nodes',
                         label_network,
                         helptext="Label nodes", icon =label_icon,
                         sensitive=has_network,
                         checked = is_label_activated))

    # Add the command to the toolbar
    main_toolbar.InsertItem("label_network")
    main_toolbar.RemoveItem("map_identify_tool")
    main_toolbar.RemoveItem("map_label_tool")
    main_toolbar.RemoveItem("mouse_position_tool")
    main_toolbar.RemoveItem("selected_full_extent")
    main_toolbar.InsertSeparator()
    main_toolbar.InsertItem("identify_node_tool")
    main_toolbar.InsertItem("move_node_tool")
    main_toolbar.InsertItem("delete_node_tool")
    main_toolbar.InsertSeparator()
    main_toolbar.InsertItem("add_link_tool")
        
class NodeTypeMenu(object):
    '''
    This class manages the NodeType menu. It can dynamically add or remove node
    types from the menu and the toolbar based on a selected project
    '''

    def __init__(self, context):
        '''
        context : a Thuban context object
        '''
        self.context = context
        self.menubar = self.context.mainwindow.GetMenuBar()
        assert isinstance(self.menubar, wx.MenuBar)
        self.mainmenu = self.menubar.GetMenu(self.menubar.FindMenu("&Objects"))
        assert isinstance(self.mainmenu, wx.Menu)
        self.toolbar = self.context.mainwindow.GetToolBar()
        assert isinstance(self.toolbar, wx.ToolBar)
        self.toolbar.AddSeparator()
        # Add the Add node type menu
        self.add_node_menu = wx.Menu()
        self.mainmenu.AppendSeparator()
        self.mainmenu.AppendMenu(-1, 'Add node', self.add_node_menu)
        # Add the Add link menu
        context.mainwindow.add_menu_command(self.mainmenu, 'add_link_tool')
        
        self.loaded_commands = dict()
        self.project =  None
        

    def load_from_project(self, project):
        '''
        Load nodetypes from the given project into the menu and the toolbar
        
        If project is not the same as the project associated to the loaded 
        commandes, it does a cleanup before. This will prevent having commands
        from multiple projects in the UI
        '''
        if self.project != project:
            # the previous project should cleanup its icons !
            self.cleanup()
            self.project = project
        # load the node types defined in this project
        for nodetype in project.nodetypes:
            self._add_nodetype(nodetype)
        # refresh the toolbar        
        self.toolbar.Realize()
        
    
    def get_cmd_name(self, nodetype):
        """
        Return the cmd name for a given nodetype
        
        FIXME : potential bug if two names have the same name 
        (this should be impossible due to the db constraints)
        """
        return (u'add'+ nodetype.name).replace(u" ", "_")        
        
        
    def _add_nodetype(self, nodetype):
        '''
        Private method that adds the command and updates the menu content
        '''
        if nodetype.visible_on_map is False:
            return
         # create command
        icon = getBitmapForNodeType(nodetype, 24, 
                    wx.Brush(self.context.mainwindow.GetBackgroundColour()))
        cmd_name = self.get_cmd_name(nodetype)
        tool_name = "AddNodeTool"+ str(nodetype.id)
        tool = ToolCommand(cmd_name, 'Add ' + nodetype.name, 
                            add_node_with_type, args=(nodetype,),
                         helptext = "Add %s" % nodetype.name , 
                         sensitive= has_network,
                         icon = icon,
                         checked = make_check_current_tool(tool_name))
        registry.Add(tool) 
        # add the command to the menu
        self.context.mainwindow.add_menu_command(self.add_node_menu, cmd_name)
        # add the command to the toolbar
        self.context.mainwindow.add_toolbar_command(self.toolbar, cmd_name)
        self.loaded_commands[nodetype.id]  = cmd_name

    def add_nodetype(self, nodetype):
        '''
        Method to call when a new nodetype is added to a project.
        
        This is mainly done by using a Subscription on 
        model.network.PROJECT_NODETYPE_ADDED message from 
        the ObjectTypeEditorDialog
        '''
        self._add_nodetype(nodetype)
        # refresh the toolbar
        self.toolbar.Realize()

    def update_nodetype(self, nodetype):
        """
        Update an existing nodetype : means updating its name and symbology
        """
        self._remove_nodetype(nodetype)
        
        # reinsert the nodetype
        self.add_nodetype(nodetype)
        
    def remove_nodetype(self, nodetype):
        """
        Remove nodetype from toolbar and update UI
        """
        self._remove_nodetype(nodetype)
        self.toolbar.Realize()
        
    def _remove_nodetype(self, nodetype):
        """
        Remove an existing nodetype 
        """
        cmd_name = self.loaded_commands [nodetype.id]
        # remove from menu
        self.context.mainwindow.remove_menu_command(self.add_node_menu, 
                                                    cmd_name)
        # remove from toolbar
        self.context.mainwindow.remove_toolbar_command(self.toolbar, 
                                                           cmd_name) 
        
    def cleanup(self):
        '''
        Remove all the menu items and toolbars commands from the menu
        '''
        for id, cmd_name in self.loaded_commands.items():
            # remove from menu
            self.context.mainwindow.remove_menu_command(self.add_node_menu, 
                                                        cmd_name)
            # remove from toolbar
            self.context.mainwindow.remove_toolbar_command(self.toolbar, 
                                                           cmd_name)
        #refresh toolbar
        self.toolbar.Realize()
        # purge loaded commands list
        self.loaded_commands = dict()
